home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright (c) 1992, Brian Berliner and Jeff Polk
- * Copyright (c) 1989-1992, Brian Berliner
- *
- * You may distribute under the terms of the GNU General Public License as
- * specified in the README file that comes with the CVS 1.4 kit.
- *
- * Add
- *
- * Adds a file or directory to the RCS source repository. For a file,
- * the entry is marked as "needing to be added" in the user's own CVS
- * directory, and really added to the repository when it is committed.
- * For a directory, it is added at the appropriate place in the source
- * repository and a CVS directory is generated within the directory.
- *
- * The -m option is currently the only supported option. Some may wish to
- * supply standard "rcs" options here, but I've found that this causes more
- * trouble than anything else.
- *
- * The user files or directories must already exist. For a directory, it must
- * not already have a CVS file in it.
- *
- * An "add" on a file that has been "remove"d but not committed will cause the
- * file to be resurrected.
- */
-
- #include "cvs.h"
- #include "savecwd.h"
-
- static int add_directory PROTO((char *repository, char *dir));
- static int build_entry PROTO((char *repository, char *user, char *options,
- char *message, List * entries, char *tag));
-
- static const char *const add_usage[] =
- {
- "Usage: %s %s [-k rcs-kflag] [-m message] files...\n",
- "\t-k\tUse \"rcs-kflag\" to add the file with the specified kflag.\n",
- "\t-m\tUse \"message\" for the creation log.\n",
- NULL
- };
-
- int
- add (argc, argv)
- int argc;
- char **argv;
- {
- char *message = NULL;
- char *user;
- int i;
- char *repository;
- int c;
- int err = 0;
- int added_files = 0;
- char *options = NULL;
- List *entries;
- Vers_TS *vers;
-
- if (argc == 1 || argc == -1)
- usage (add_usage);
-
- wrap_setup ();
-
- /* parse args */
- optind = 1;
- while ((c = getopt (argc, argv, "k:m:")) != -1)
- {
- switch (c)
- {
- case 'k':
- if (options)
- free (options);
- options = RCS_check_kflag (optarg);
- break;
-
- case 'm':
- message = xstrdup (optarg);
- break;
- case '?':
- default:
- usage (add_usage);
- break;
- }
- }
- argc -= optind;
- argv += optind;
-
- if (argc <= 0)
- usage (add_usage);
-
- /* find the repository associated with our current dir */
- repository = Name_Repository ((char *) NULL, (char *) NULL);
-
- #ifdef CLIENT_SUPPORT
- if (client_active)
- {
- int i;
- start_server ();
- ign_setup ();
- if (options) send_arg(options);
- option_with_arg ("-m", message);
- for (i = 0; i < argc; ++i)
- /* FIXME: Does this erroneously call Create_Admin in error
- conditions which are only detected once the server gets its
- hands on things? */
- if (isdir (argv[i]))
- {
- char *tag;
- char *date;
- char *rcsdir = xmalloc (strlen (repository)
- + strlen (argv[i]) + 10);
-
- /* before we do anything else, see if we have any
- per-directory tags */
- ParseTag (&tag, &date);
-
- sprintf (rcsdir, "%s/%s", repository, argv[i]);
-
- Create_Admin (argv[i], argv[i], rcsdir, tag, date);
-
- if (tag)
- free (tag);
- if (date)
- free (date);
- free (rcsdir);
- }
- send_file_names (argc, argv, SEND_EXPAND_WILD);
- send_files (argc, argv, 0, 0);
- send_to_server ("add\012", 0);
- return get_responses_and_close ();
- }
- #endif
-
- entries = Entries_Open (0);
-
- /* walk the arg list adding files/dirs */
- for (i = 0; i < argc; i++)
- {
- int begin_err = err;
- int begin_added_files = added_files;
-
- user = argv[i];
- strip_trailing_slashes (user);
- if (strchr (user, '/') != NULL)
- {
- error (0, 0,
- "cannot add files with '/' in their name; %s not added", user);
- err++;
- continue;
- }
-
- vers = Version_TS (repository, options, (char *) NULL, (char *) NULL,
- user, 0, 0, entries, (RCSNode *) NULL);
- if (vers->vn_user == NULL)
- {
- /* No entry available, ts_rcs is invalid */
- if (vers->vn_rcs == NULL)
- {
- /* There is no RCS file either */
- if (vers->ts_user == NULL)
- {
- /* There is no user file either */
- error (0, 0, "nothing known about %s", user);
- err++;
- }
- else if (!isdir (user) || wrap_name_has (user, WRAP_TOCVS))
- {
- /*
- * See if a directory exists in the repository with
- * the same name. If so, blow this request off.
- */
- char dname[PATH_MAX];
- (void) sprintf (dname, "%s/%s", repository, user);
- if (isdir (dname))
- {
- error (0, 0,
- "cannot add file `%s' since the directory",
- user);
- error (0, 0, "`%s' already exists in the repository",
- dname);
- error (1, 0, "illegal filename overlap");
- }
-
- /* There is a user file, so build the entry for it */
- if (build_entry (repository, user, vers->options,
- message, entries, vers->tag) != 0)
- err++;
- else
- {
- added_files++;
- if (!quiet)
- {
- if (vers->tag)
- error (0, 0, "\
- scheduling %s `%s' for addition on branch `%s'",
- (wrap_name_has (user, WRAP_TOCVS)
- ? "wrapper"
- : "file"),
- user, vers->tag);
- else
- error (0, 0, "scheduling %s `%s' for addition",
- (wrap_name_has (user, WRAP_TOCVS)
- ? "wrapper"
- : "file"),
- user);
- }
- }
- }
- }
- else if (RCS_isdead (vers->srcfile, vers->vn_rcs))
- {
- if (isdir (user) && !wrap_name_has (user, WRAP_TOCVS))
- {
- error (0, 0, "the directory `%s' cannot be added because a file of the", user);
- error (1, 0, "same name already exists in the repository.");
- }
- else
- {
- if (vers->tag)
- error (0, 0, "file `%s' will be added on branch `%s' from version %s",
- user, vers->tag, vers->vn_rcs);
- else
- error (0, 0, "version %s of `%s' will be resurrected",
- vers->vn_rcs, user);
- Register (entries, user, "0", vers->ts_user, NULL,
- vers->tag, NULL, NULL);
- ++added_files;
- }
- }
- else
- {
- /*
- * There is an RCS file already, so somebody else must've
- * added it
- */
- error (0, 0, "%s added independently by second party", user);
- err++;
- }
- }
- else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
- {
-
- /*
- * An entry for a new-born file, ts_rcs is dummy, but that is
- * inappropriate here
- */
- error (0, 0, "%s has already been entered", user);
- err++;
- }
- else if (vers->vn_user[0] == '-')
- {
- /* An entry for a removed file, ts_rcs is invalid */
- if (vers->ts_user == NULL)
- {
- /* There is no user file (as it should be) */
- if (vers->vn_rcs == NULL)
- {
-
- /*
- * There is no RCS file, so somebody else must've removed
- * it from under us
- */
- error (0, 0,
- "cannot resurrect %s; RCS file removed by second party", user);
- err++;
- }
- else
- {
-
- /*
- * There is an RCS file, so remove the "-" from the
- * version number and restore the file
- */
- char *tmp = xmalloc (strlen (user) + 50);
-
- (void) strcpy (tmp, vers->vn_user + 1);
- (void) strcpy (vers->vn_user, tmp);
- (void) sprintf (tmp, "Resurrected %s", user);
- Register (entries, user, vers->vn_user, tmp, vers->options,
- vers->tag, vers->date, vers->ts_conflict);
- free (tmp);
-
- /* XXX - bugs here; this really resurrect the head */
- /* Note that this depends on the Register above actually
- having written Entries, or else it won't really
- check the file out. */
- if (update (2, argv + i - 1) == 0)
- {
- error (0, 0, "%s, version %s, resurrected", user,
- vers->vn_user);
- }
- else
- {
- error (0, 0, "could not resurrect %s", user);
- err++;
- }
- }
- }
- else
- {
- /* The user file shouldn't be there */
- error (0, 0, "%s should be removed and is still there (or is back again)", user);
- err++;
- }
- }
- else
- {
- /* A normal entry, ts_rcs is valid, so it must already be there */
- error (0, 0, "%s already exists, with version number %s", user,
- vers->vn_user);
- err++;
- }
- freevers_ts (&vers);
-
- /* passed all the checks. Go ahead and add it if its a directory */
- if (begin_err == err
- && isdir (user)
- && !wrap_name_has (user, WRAP_TOCVS))
- {
- err += add_directory (repository, user);
- continue;
- }
- #ifdef SERVER_SUPPORT
- if (server_active && begin_added_files != added_files)
- server_checked_in (user, ".", repository);
- #endif
- }
- if (added_files)
- error (0, 0, "use 'cvs commit' to add %s permanently",
- (added_files == 1) ? "this file" : "these files");
-
- Entries_Close (entries);
-
- if (message)
- free (message);
-
- return (err);
- }
-
- /*
- * The specified user file is really a directory. So, let's make sure that
- * it is created in the RCS source repository, and that the user's directory
- * is updated to include a CVS directory.
- *
- * Returns 1 on failure, 0 on success.
- */
- static int
- add_directory (repository, dir)
- char *repository;
- char *dir;
- {
- char rcsdir[PATH_MAX];
- struct saved_cwd cwd;
- char message[PATH_MAX + 100];
- char *tag, *date;
-
- if (strchr (dir, '/') != NULL)
- {
- error (0, 0,
- "directory %s not added; must be a direct sub-directory", dir);
- return (1);
- }
- if (strcmp (dir, CVSADM) == 0)
- {
- error (0, 0, "cannot add a `%s' directory", CVSADM);
- return (1);
- }
-
- /* before we do anything else, see if we have any per-directory tags */
- ParseTag (&tag, &date);
-
- /* now, remember where we were, so we can get back */
- if (save_cwd (&cwd))
- return (1);
- if (chdir (dir) < 0)
- {
- error (0, errno, "cannot chdir to %s", dir);
- return (1);
- }
- #ifdef SERVER_SUPPORT
- if (!server_active && isfile (CVSADM))
- #else
- if (isfile (CVSADM))
- #endif
- {
- error (0, 0, "%s/%s already exists", dir, CVSADM);
- goto out;
- }
-
- (void) sprintf (rcsdir, "%s/%s", repository, dir);
- if (isfile (rcsdir) && !isdir (rcsdir))
- {
- error (0, 0, "%s is not a directory; %s not added", rcsdir, dir);
- goto out;
- }
-
- /* setup the log message */
- (void) sprintf (message, "Directory %s added to the repository\n", rcsdir);
- if (tag)
- {
- (void) strcat (message, "--> Using per-directory sticky tag `");
- (void) strcat (message, tag);
- (void) strcat (message, "'\n");
- }
- if (date)
- {
- (void) strcat (message, "--> Using per-directory sticky date `");
- (void) strcat (message, date);
- (void) strcat (message, "'\n");
- }
-
- if (!isdir (rcsdir))
- {
- mode_t omask;
- Node *p;
- List *ulist;
-
- #if 0
- char line[MAXLINELEN];
-
- (void) printf ("Add directory %s to the repository (y/n) [n] ? ",
- rcsdir);
- (void) fflush (stdout);
- clearerr (stdin);
- if (fgets (line, sizeof (line), stdin) == NULL ||
- (line[0] != 'y' && line[0] != 'Y'))
- {
- error (0, 0, "directory %s not added", rcsdir);
- goto out;
- }
- #endif
-
- omask = umask (cvsumask);
- if (CVS_MKDIR (rcsdir, 0777) < 0)
- {
- error (0, errno, "cannot mkdir %s", rcsdir);
- (void) umask (omask);
- goto out;
- }
- (void) umask (omask);
-
- /*
- * Set up an update list with a single title node for Update_Logfile
- */
- ulist = getlist ();
- p = getnode ();
- p->type = UPDATE;
- p->delproc = update_delproc;
- p->key = xstrdup ("- New directory");
- p->data = (char *) T_TITLE;
- (void) addnode (ulist, p);
- Update_Logfile (rcsdir, message, (char *) NULL, (FILE *) NULL, ulist);
- dellist (&ulist);
- }
-
- #ifdef SERVER_SUPPORT
- if (!server_active)
- Create_Admin (".", dir, rcsdir, tag, date);
- #else
- Create_Admin (".", dir, rcsdir, tag, date);
- #endif
- if (tag)
- free (tag);
- if (date)
- free (date);
-
- (void) printf ("%s", message);
- out:
- if (restore_cwd (&cwd, NULL))
- exit (EXIT_FAILURE);
- free_cwd (&cwd);
- return (0);
- }
-
- /*
- * Builds an entry for a new file and sets up "CVS/file",[pt] by
- * interrogating the user. Returns non-zero on error.
- */
- static int
- build_entry (repository, user, options, message, entries, tag)
- char *repository;
- char *user;
- char *options;
- char *message;
- List *entries;
- char *tag;
- {
- char fname[PATH_MAX];
- char line[MAXLINELEN];
- FILE *fp;
-
- if (noexec)
- return (0);
-
- /*
- * The requested log is read directly from the user and stored in the
- * file user,t. If the "message" argument is set, use it as the
- * initial creation log (which typically describes the file).
- */
- (void) sprintf (fname, "%s/%s%s", CVSADM, user, CVSEXT_LOG);
- fp = open_file (fname, "w+");
- if (message && fputs (message, fp) == EOF)
- error (1, errno, "cannot write to %s", fname);
- if (fclose(fp) == EOF)
- error(1, errno, "cannot close %s", fname);
-
- /*
- * Create the entry now, since this allows the user to interrupt us above
- * without needing to clean anything up (well, we could clean up the
- * ,t file, but who cares).
- */
- (void) sprintf (line, "Initial %s", user);
- Register (entries, user, "0", line, options, tag, (char *) 0, (char *) 0);
- return (0);
- }
-